package com.kefu.chat.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.dfa.WordTree;
import cn.hutool.http.HtmlUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Maps;
import com.kefu.basic.model.BasicCommuteConfig;
import com.kefu.basic.service.BasicShareService;
import com.kefu.basic.util.IpCityUtil;
import com.kefu.basic.util.IpCityUtil.Address;
import com.kefu.chat.model.*;
import com.kefu.chat.smart.ChatConnectEvent;
import com.kefu.chat.util.ChatConstant;
import com.kefu.chat.util.RongCloudUtils;
import com.kefu.chat.vo.*;
import com.kefu.chat.vo.ChatEventMsg.*;
import com.kefu.chat.vo.ChatUserParam.*;
import com.kefu.common.exception.BusinessException;
import com.kefu.common.multipart.IMultipartProvider;
import com.kefu.common.multipart.MultiPart;
import com.kefu.common.trigger.TriggerWrapper;
import com.kefu.common.util.Assert;
import com.kefu.common.util.JacksonUtil;
import com.kefu.crm.mapper.CrmCustomerMapper;
import com.kefu.crm.model.CrmCustomer;
import com.kefu.framework.config.KefuProperties;
import com.kefu.framework.web.http.IpAddressUtil;
import com.kefu.robot.mapper.RobotDocCommentMapper;
import com.kefu.robot.mapper.RobotDocMapper;
import com.kefu.robot.model.RobotDoc;
import com.kefu.robot.model.RobotDocComment;
import com.kefu.smart.service.SmartShareService;
import com.kefu.sys.mapper.SysGroupMapper;
import com.kefu.sys.model.SysGroup;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class ChatUserService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private ChatCacheService chatCacheService;
    @Resource
    private RobotDocCommentMapper robotDocCommentMapper;
    @Autowired
    private ChatAdminService chatAdminService;
    @Autowired
    private ChatSessionService chatSessionService;
    @Autowired
    private ChatSessionMsgService chatSessionMsgService;
    @Autowired
    private IMultipartProvider multipartProvider;
    @Resource
    private SysGroupMapper sysGroupMapper;
    @Autowired
    private ChatChannelService chatChannelService;
    @Autowired
    private ChatLeaveMsgService chatLeaveMsgService;
    @Autowired
    BasicShareService basicShareService;
    @Autowired
    SmartShareService smartShareService;
    @Resource
    RobotDocMapper robotDocMapper;
    @Resource
    private CrmCustomerMapper crmCustomerMapper;
    @Autowired
    KefuProperties kefuProperties;
    @Autowired
    ChatReceptConfigService chatReceptConfigService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    public UserInitR init(HttpServletRequest request, UserInitP param) {
        ChatChannel channel = chatChannelService.getFromId(param.getChannelId());
        if (channel == null) {
            return null;
        }
        String userId = param.getUserId();
        String pid = param.getPid();
        String tenantId = channel.getTenantId();
        if (StrUtil.isNotBlank(pid)) {
            //如果有对接id，根据对接id生成用户id
            userId = SecureUtil.md5(tenantId + pid);
        }
        ChatUser oldUser = new ChatUser();
        if (StrUtil.isNotBlank(userId)) {
            ChatUser userCache = chatCacheService.getUser(userId);
            if (userCache != null) {
                if (userCache.getStatus() != ChatConstant.user_status_offline) {
                    UserOutE msg = new UserOutE();
                    msg.setOutType(ChatConstant.out_type_reopen);
                    chatCacheService.sendEventMsg(userCache.getPollId(), msg);
                    userCache.setPollId(IdUtil.simpleUUID());
                    chatCacheService.setUser(userCache);
                    logger.info("user_init_refresh:{},{}", param, userCache);
                    return BeanUtil.toBean(userCache, UserInitR.class);
                }
                oldUser = userCache;
            }
        } else {
            userId = IdUtil.simpleUUID();
        }
        //缓存中没有，或者上次会话已经离线，生成新的用户缓存，生成会话id，生成轮询id
        ChatUser user = BeanUtil.toBean(param, ChatUser.class);
        user.setUserId(userId);
        user.setSessionId(IdUtil.simpleUUID());
        user.setTenantId(tenantId);
        user.setPollId(IdUtil.simpleUUID());
        user.setChannelType(channel.getChannelType());
        user.setChannelName(channel.getChannelName());
        user.setUserAgent(request.getHeader("User-Agent"));
        user.setReferer(request.getHeader("Referer"));
        if(StringUtils.isNotBlank(param.getReferer())){
    		user.setReferer(param.getReferer());
    	}
    	
    	LocaleContextHolder.setLocale(new Locale(param.getLocale()));
    	user.setUserIp(IpAddressUtil.getIpAddr(request));
    	Address address=IpCityUtil.getIpAddr(user.getUserIp());
    	user.setCountry(address.getCountry());
    	user.setCity(address.getCity());
        user.setUserIp(IpAddressUtil.getIpAddr(request));
        user.setIpAddress(IpCityUtil.getIpCity(user.getUserIp()));
        if (StrUtil.isNotBlank(param.getUserName())) {
            user.setUserName(param.getUserName());
        } else if (StrUtil.isNotBlank(oldUser.getUserName())) {
            user.setUserName(oldUser.getUserName());
        } else {
            user.setUserName(user.getIpAddress() + "客户");
        }
        user.setPhone(StrUtil.isNotBlank(param.getPhone()) ? param.getPhone() : oldUser.getPhone());
        user.setEmail(StrUtil.isNotBlank(param.getEmail()) ? param.getEmail() : oldUser.getEmail());
        user.setCustomerId(oldUser.getCustomerId());
        user.setLastAdminId(oldUser.getAdminId());
        user.setUserFormMode(oldUser.getUserFormMode());
        user.setMarkMode(oldUser.getMarkMode());
        user.setBlackMode(oldUser.getBlackMode());
        chatCacheService.setUser(user);
        logger.info("user_init_end:{},{}", param, user);
        return BeanUtil.toBean(user, UserInitR.class);
    }

    public UserConnectR connect(UserConnectP param) {
        UserConnectR result = new UserConnectR();
        result.setStatus(1);
        ChatUser user = chatCacheService.getUser(param.getUserId());
        logger.info("user_connect:{},{}", param, JacksonUtil.toString(user));
        if (user == null) {
            result.setStatus(ChatConstant.user_status_offline);
            return result;
        }
        if (user.getStatus() == ChatConstant.user_status_online) {
            ChatAdmin admin = chatCacheService.getAdmin(user.getAdminId());
            result.setStatus(user.getStatus());
            result.setAdminNick(admin.getAdminNick());
            result.setAdminId(admin.getAdminId());
            return result;
        }
        if (user.getStatus() == ChatConstant.user_status_wait) {
            result.setStatus(user.getStatus());
            return result;
        }
        if (user.getOfflineTime() > 0) {
            //已经结束的会话不能再转人工
            result.setStatus(ChatConstant.user_status_offline);
            return result;
        }
        if(user.getBlackMode() == 1){
            // 已经拉黑的客户，不能转人工
            result.setStatus(ChatConstant.user_status_black);
            return result;
        }
        //判断上下班时间
        boolean working = isWorking(user, param.getGroupId());
        if (!working) {
            result.setStatus(ChatConstant.user_status_no_working_time);
            return result;
        }
        //如果没有登录的客服，提示无客服返回
        long allAdminSize = chatCacheService.getAdminSetSize(user.getTenantId());
        if (allAdminSize < 1) {
            result.setStatus(ChatConstant.user_status_robot);
            return result;
        }
        CrmCustomer crmCustomer = null;
        if (StrUtil.isNotBlank(user.getCustomerId())) {
            crmCustomer = crmCustomerMapper.selectByPrimaryKey(user.getCustomerId());
        }
        //智能路由分配
        Map<String, Object> map = Maps.newHashMap();
        map.put("chatUser", user);
        map.put("customer", crmCustomer);
        TriggerWrapper<UserConnectP, UserConnectR> triggerWrapper = TriggerWrapper.<UserConnectP, UserConnectR>builder()
                .type(ChatConnectEvent.TYPE).tenantId(user.getTenantId()).newObj(param).oldObj(param).actionObj(result).map(map).build();
        smartShareService.trigger(triggerWrapper, ChatConnectEvent.TYPE, null, ChatConnectEvent.EventType.Incoming.name());
        logger.info("user_connect_trigger:{},{}", param, result.getActions());
        UserConnectR resultTrigger;
        for (int i = 0; i < result.getActions().size(); i++) {
            UserConnectP action = result.getActions().get(i);
            resultTrigger = tryConnect(user, action, action.isQueue());
            //智能路由满足条件或者是最后一个，直接返回
            if (resultTrigger.getStatus() != 1 || i == result.getActions().size() - 1) {
                return resultTrigger;
            }
        }
        //熟客分配
        ChatChannel channel = chatChannelService.getFromId(user.getChannelId());
        if (Objects.equals(channel.getChooseLastAdminMode(), 1)) {
            param.setAdminId(user.getLastAdminId());
        }
        //如果智能路由不满足条件，进入默认流程
        return tryConnect(user, param, true);

    }

    public UserConnectR tryConnect(ChatUser user, UserConnectP param, boolean queue) {
        logger.info("user_connect_try:{},{},{}", param, queue, user);
        UserConnectR result = new UserConnectR();
        result.setStatus(1);

        if (StrUtil.isNotBlank(param.getGroupId())) {
            user.setGroupId(param.getGroupId());
        }
        if (StrUtil.isNotBlank(param.getAdminId())) {
            user.setAdminId(param.getAdminId());
        }
        boolean hasWait = false;
        /*
        //判断是否有相同技能组内的排队，如果已经有排队的，也需要进入排队
        List<String> waitIdList = chatCacheService.getWaitList(user.getTenantId());
        for (String waitId : waitIdList) {
            ChatUser waitUser = chatCacheService.getUser(waitId);
            if (StrUtil.isBlank(user.getGroupId())) {
                if (StrUtil.isBlank(waitUser.getGroupId())) {
                    hasWait = true;
                    break;
                }
            } else {
                if (StrUtil.isNotBlank(waitUser.getGroupId()) && waitUser.getGroupId().equals(user.getGroupId())) {
                    hasWait = true;
                    break;
                }
            }
        }
        */
        if (!hasWait) {
            ChatAdmin admin = chooseAdmin(user, param);
            if (admin != null) {
                //选择到合适的客服，建立与客服连接
                if (StrUtil.isNotBlank(user.getGroupId())) {
                    SysGroup sysGroup = sysGroupMapper.selectById(user.getGroupId());
                    if (sysGroup != null) {
                        user.setGroupName(sysGroup.getGroupName());
                    }
                }
                chatSessionService.chatConnect(user, admin, ChatConstant.connect_type_direct);
                result.setStatus(2);
                result.setAdminNick(admin.getAdminNick());
                result.setAdminFace(admin.getAdminFace());
                result.setAdminId(admin.getAdminId());
                chatCacheService.setUserPollTime(user.getTenantId(), param.getUserId());
                return result;
            }
        }
        if (queue) {
            //没有合适的客服，进入排队
            chatCacheService.delRobotSet(user.getTenantId(), user.getSessionId());
            long position = chatCacheService.addWaitList(user);
            user.setStatus(ChatConstant.user_status_wait);
            user.setWaitTime(System.currentTimeMillis());
            if (user.getStartTime() == 0) user.setStartTime(user.getWaitTime());
            chatCacheService.setUser(user);
            result.setStatus(ChatConstant.user_status_wait);
            result.setWaitIndex(position);
            chatCacheService.setUserPollTime(user.getTenantId(), param.getUserId());
            logger.info("user_connect_wait:{},{},{}", param, user, result);
            return result;
        }

        return result;
    }

    /**
     * 判断是否为工作时间
     */
    private boolean isWorking(ChatUser user, String groupId) {

        //如果没有技能组，走公司id配置
        if (StrUtil.isBlank(groupId)) {
            groupId = user.getTenantId();
        }

        List<BasicCommuteConfig> list = basicShareService.searchCommuteConfigByGroupId(groupId, 0);
        Calendar calendar = Calendar.getInstance();
        String date = new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime());
        String time = new SimpleDateFormat("HH:mm").format(calendar.getTime());
        int week = calendar.get(Calendar.DAY_OF_WEEK);

        if (list.size() == 0) {
            return true;
        }

        boolean workOvertime = list.stream().filter(config -> config.getStrategyType() == 3)
                .filter(config -> config.getStartDate().compareTo(date) <= 0 && config.getEndDate().compareTo(date) >= 0)
                .anyMatch(config -> config.getStartTime().compareTo(time) <= 0 && config.getEndTime().compareTo(time) >= 0);
        if (workOvertime) {//特殊加班
            return true;
        }
        boolean holiday = list.stream().filter(config -> config.getStrategyType() == 2)
                .filter(config -> config.getStartDate().compareTo(date) <= 0 && config.getEndDate().compareTo(date) >= 0)
                .anyMatch(config -> config.getStartTime().compareTo(time) <= 0 && config.getEndTime().compareTo(time) >= 0);
        if (holiday) {//节假日
            return false;
        }

        boolean workTime = list.stream().filter(config -> config.getStrategyType() == 1)
                .filter(config -> config.getWeekStrategy().contains(String.valueOf(week == 1 ? 7 : week - 1)))//周日是1 周六是7 这里做转换 周一是1 周日是7
                .anyMatch(config -> config.getStartTime().compareTo(time) <= 0 && config.getEndTime().compareTo(time) >= 0);
        if (workTime) {//正常上班
            return true;
        }
        return false;
    }


    ChatAdmin chooseAdmin(ChatUser user, UserConnectP param) {
        //熟客分配
        if (StrUtil.isNotBlank(param.getAdminId())) {
            ChatAdmin admin = chatCacheService.getAdmin(param.getAdminId());
            if (admin != null && admin.getStatus() == ChatConstant.admin_status_online) {
                long currentAccept = chatCacheService.getAdminUserSetSize(admin.getAdminId());
                boolean acceptFlag = currentAccept < admin.getMaxAccept();
                boolean groupFlag = true;
                //判断所选坐席是否包含所选技能组，不包含的话不直接选择该坐席
                if (StrUtil.isNotBlank(param.getGroupId()) && !admin.getGroupId().contains(param.getGroupId())) {
                    groupFlag = false;
                }
                logger.info("user_chooseAdmin_指定坐席:{},{},{},{},{},{}", param, user, admin, currentAccept, acceptFlag, groupFlag);
                if (acceptFlag && groupFlag) {
                    return admin;
                }
            }
        }
        //按照饱和度分配
        Set<ChatAdmin> adminSet = chatAdminService.getAdminSet(user.getTenantId(), param.getGroupId());
        List<ChooseAdminSort> adminSorts = new ArrayList<>();
        ChatAdmin chooseAdmin = null;
        for (ChatAdmin admin : adminSet) {
            if (admin.getMaxAccept() == 0) {
                continue;
            }
            if (admin.getStatus() == ChatConstant.admin_status_online) {
                ChooseAdminSort sort = new ChooseAdminSort();
                sort.setUid(admin.getAdminId());
                sort.setMaxAccept(admin.getMaxAccept());
                sort.setCurrentAccept(chatCacheService.getAdminUserSetSize(admin.getAdminId()));
                adminSorts.add(sort);
            }
        }
        //按饱和度，选择接待量最少的客服
        if (adminSorts.size() > 0) {
            Collections.shuffle(adminSorts);
            ChooseAdminSort min = Collections.min(adminSorts);
            if (min.getMaxAccept() > min.getCurrentAccept()) {
                chooseAdmin = chatCacheService.getAdmin(min.getUid());
            }
        }
        logger.info("user_chooseAdmin:{},{},{},{}", param, user, adminSorts, chooseAdmin);
        return chooseAdmin;
    }

    public UserSendMsgR sendMsg(UserSendMsgP param) {
        UserSendMsgR result = new UserSendMsgR();
        ChatUser user = chatCacheService.getUser(param.getUserId());
        if (user == null || user.getStatus() != ChatConstant.user_status_online) {
            result.setStatus("0");
            return result;
        }
        ChatAdmin admin = chatCacheService.getAdmin(user.getAdminId());
        AdminGetMsgE msg = BeanUtil.toBean(user, AdminGetMsgE.class);
        long time = System.currentTimeMillis();
        String msgId = IdUtil.simpleUUID();
        msg.setMsgId(msgId);
        msg.setMsgType(param.getMsgType());
        msg.setMsgContent(param.getMsgContent());
        msg.setFileName(param.getFileName());
        msg.setAudioDuration(param.getAudioDuration());
        msg.setMsgTime(time);
        Set<String> wordList = basicShareService.getSensitiveWord(user.getTenantId(), 2);
        WordTree wordTree = new WordTree();
        wordTree.addWords(wordList);
        List<String> sensitiveList = wordTree.matchAll(param.getMsgContent(), -1, false, true);
        if (CollUtil.isEmpty(sensitiveList)) {
            msg.setSensitiveFlag(0);
        } else {
            msg.setSensitiveFlag(1);
            result.setSensitiveWords(sensitiveList);
            msg.setSensitiveWords(String.join(",", sensitiveList));
        }
        chatCacheService.sendEventMsg(admin.getPollId(), msg);
        ChatSessionMsg msgLog = ChatSessionMsg.builder()
                .id(msgId)
                .userId(user.getUserId())
                .sessionId(user.getSessionId())
                .tenantId(user.getTenantId())
                .sender(user.getUserId())
                .senderName(user.getUserName())
                .senderType(ChatConstant.msg_sender_user)
                .receiveType(ChatConstant.msg_sender_admin)
                .receive(user.getAdminId())
                .msgType(param.getMsgType())
                .msgContent(param.getMsgContent())
                .fileName(param.getFileName())
                .audioDuration(param.getAudioDuration())
                .msgTime(System.currentTimeMillis())
                .adminId(user.getAdminId())
                .sensitiveFlag(msg.getSensitiveFlag())
                .sensitiveWords(msg.getSensitiveWords())
                .clientType(ChatConstant.msg_client_all_type)
                .build();
        chatSessionMsgService.insertSessionMsg(msgLog);
        logger.info("user_sendMsg:{},{},{}", param, user, admin);
        result.setStatus("1");
        return result;
    }

    public UserGetEventMsgR getEventMsg(UserGetEventMsgP param) {
        UserGetEventMsgR result = new UserGetEventMsgR();
        ChatUser user = chatCacheService.getUser(param.getUserId());
        List<Map<String, Object>> eventMsgList = chatCacheService.getEventMsg(param.getPollId());
        result.setMsgList(eventMsgList);
        result.setStatus(user.getStatus());
        result.setAdminNick(user.getAdminNick());
        if (user.getStatus() == ChatConstant.user_status_wait) {
            result.setWaitIndex(chatCacheService.getWaitIndex(user.getTenantId(), user.getUserId()));
            chatCacheService.setUserPollTime(user.getTenantId(), param.getUserId());
        }
        if (user.getStatus() == ChatConstant.user_status_online) {
            chatCacheService.setUserPollTime(user.getTenantId(), param.getUserId());
        }
        if (CollUtil.isNotEmpty(result.getMsgList())) {
            logger.info("user_getEventMsg:{},{}", user, result);
        }
        return result;
    }

    public UserGetEventMsgR getOfflineMsg(UserGetEventMsgP param) {
        UserGetEventMsgR result = new UserGetEventMsgR();
        //查询离线消息
        result.setMsgList(chatCacheService.getOfflineMsg(param.getUserId()));
        return result;
    }

    public List<UserGetSessionMsgR> getSessionMsg(UserGetSessionMsgP param) {
        List<UserGetSessionMsgR> result = new ArrayList<>();
        List<ChatSessionMsg> msgList = chatSessionMsgService.getSessionMsg(param.getSessionId());
        for (ChatSessionMsg sessionMsg : msgList) {
            UserGetSessionMsgR msg = BeanUtil.toBean(sessionMsg, UserGetSessionMsgR.class);
            String msgContent = msg.getMsgContent();
            String docId = msg.getDocId();
            //移动端，机器人词条富文本处理
            if (param.getCleanHtmlTag() == 1 && StrUtil.isNotBlank(docId) && StrUtil.isNotBlank(msgContent)) {
                msg.setMsgContent(HtmlUtil.cleanHtmlTag(msgContent));
                if (msgContent.contains("http")) {
                    msg.setHtmlUrl(kefuProperties.getDomain()+"/chat_online/question?docId=" + msg.getDocId() + "&userId=" + param.getUserId());
                }
            }
            if (Objects.equals(sessionMsg.getReadFlag(), 0)) {
                ThreadUtil.execute(() -> {
                    //给客服发送消息已读
                    ChatUser user = chatCacheService.getUser(param.getUserId());
                    if(user!=null && StrUtil.isNotBlank(user.getLastAdminId()) && user.getStatus() == ChatConstant.user_status_online ){
                        ChatAdmin admin = chatCacheService.getAdmin(user.getLastAdminId());
                        if (admin!=null && admin.getStatus()!=ChatConstant.admin_status_offline){
                            AdminGetMsgRead msgRead = new AdminGetMsgRead();
                            msgRead.setMsgId(sessionMsg.getId());
                            msgRead.setSessionId(sessionMsg.getSessionId());
                            msgRead.setUserId(sessionMsg.getUserId());
                            chatCacheService.sendEventMsg(admin.getPollId(),msgRead);
                        }
                    }
                    //更新聊天消息为已读
                    chatSessionMsgService.update(Wrappers.lambdaUpdate(ChatSessionMsg.class).set(ChatSessionMsg::getReadFlag, 1)
                            .eq(ChatSessionMsg::getId, sessionMsg.getId()));
                });
            }
            result.add(msg);
        }
        return result;
    }

    public List<UserGetSessionMsgR> getHistoryMsg(UserGetHistoryMsgP param) {
        List<UserGetSessionMsgR> result = new ArrayList<>();
        List<ChatSessionMsg> msgList = chatSessionMsgService.getHistorySessionMsg(param.getUserId(), param.getMaxTime(), param.getPageSize());
        for (ChatSessionMsg sessionMsg : msgList) {
            UserGetSessionMsgR msg = BeanUtil.toBean(sessionMsg, UserGetSessionMsgR.class);
            String msgContent = msg.getMsgContent();
            String docId = msg.getDocId();
            //移动端，机器人词条富文本处理
            if (param.getCleanHtmlTag() == 1 && StrUtil.isNotBlank(docId) && StrUtil.isNotBlank(msgContent)) {
                msg.setMsgContent(HtmlUtil.cleanHtmlTag(msgContent));
                if (msgContent.contains("http")) {
                    msg.setHtmlUrl(kefuProperties.getDomain()+"/chat_online/question?docId=" + msg.getDocId() + "&userId=" + param.getUserId());
                }
            }
            if (Objects.equals(sessionMsg.getReadFlag(), 0)) {
                ThreadUtil.execute(() -> {
                    //给客服发送消息已读
                    ChatUser user = chatCacheService.getUser(param.getUserId());
                    if(user!=null && StrUtil.isNotBlank(user.getLastAdminId()) && user.getStatus() == ChatConstant.user_status_online ){
                        ChatAdmin admin = chatCacheService.getAdmin(user.getLastAdminId());
                        if (admin!=null && admin.getStatus()!=ChatConstant.admin_status_offline){
                            AdminGetMsgRead msgRead = new AdminGetMsgRead();
                            msgRead.setMsgId(sessionMsg.getId());
                            msgRead.setSessionId(sessionMsg.getSessionId());
                            msgRead.setUserId(sessionMsg.getUserId());
                            chatCacheService.sendEventMsg(admin.getPollId(),msgRead);
                        }
                    }
                    //更新聊天消息为已读
                    chatSessionMsgService.update(Wrappers.lambdaUpdate(ChatSessionMsg.class).set(ChatSessionMsg::getReadFlag, 1)
                            .eq(ChatSessionMsg::getId, sessionMsg.getId()));
                });
            }
            result.add(msg);
        }
        return result;
    }

    public void out(String userId, int outType) {
        ChatUser user = chatCacheService.getUser(userId);
        logger.info("user_out_start:{},{},{}", userId, user, outType);
        if (user == null) {
            return;
        }
        chatCacheService.delUserPollTime(user.getTenantId(), user.getUserId());
        if (user.getStatus() == ChatConstant.user_status_offline) {
            if (StrUtil.isNotBlank(user.getAdminId())) {
                logger.info("user_out_delAdminUserSet_offline:{},{}", user, outType);
                chatCacheService.delAdminUserSet(user.getAdminId(), userId);
            }
            return;
        }
        ChatSession chatSession = BeanUtil.toBean(user, ChatSession.class);
        long currentTime = System.currentTimeMillis();
        int userStatus = user.getStatus();
        String sessionId = user.getSessionId();
        String tenantId = user.getTenantId();
        if (userStatus == ChatConstant.user_status_robot) {
            chatCacheService.delRobotSet(tenantId, sessionId);
        } else if (userStatus == ChatConstant.user_status_wait) {
            chatCacheService.delWaitList(tenantId, userId);
        } else if (userStatus == ChatConstant.user_status_online) {
            //如果不是客服主动结束会话，通知客服用户下线
            if (outType != ChatConstant.out_type_remove) {
                ChatAdmin admin = chatCacheService.getAdmin(user.getAdminId());
                logger.info("user_out_delAdminUserSet:{},{},{}", user, admin, outType);
                chatCacheService.delAdminUserSet(user.getAdminId(), userId);
                AdminUserOfflineE msg = new AdminUserOfflineE();
                msg.setUserId(userId);
                msg.setSessionId(sessionId);
                chatCacheService.sendEventMsg(admin.getPollId(), msg);
                chatAdminService.awakeWaitList(admin);
            }
        }
        if (userStatus != ChatConstant.user_status_robot) {
            UserOutE msg = new UserOutE();
            msg.setOutType(outType);
            chatCacheService.sendEventMsg(user.getPollId(), msg);
        }
        if (user.getRobotNo() > 0) {
            chatSession.setRobotMode(1);
        }
        if (user.getWaitTime() > 0) {
            chatSession.setWaitMode(1);
        }
        chatSession.setOutType(outType);
        chatSession.setOfflineTime(currentTime);
        chatSession.setTotalTime((chatSession.getOfflineTime() - chatSession.getStartTime()) / 1000);
        chatSession.setSessionStatus(1);
        ChatData chatData = chatCacheService.getData(sessionId);
        if (chatData != null) {
            chatSession.setUserMsgCount(chatData.getUserMsgCount());
            chatSession.setAdminMsgCount(chatData.getAdminMsgCount());
            chatSession.setUserAdminMsgCount(chatData.getUserAdminMsgCount());
            chatSession.setRobotMsgCount(chatData.getRobotMsgCount());
            List<Long> replyDuration = chatData.getReplyDuration();
            if (replyDuration.size() > 0) {
                double avgReplyDuration = replyDuration.stream().mapToLong(Long::longValue).average().orElse(0);
                chatSession.setFirstReplyDuration(replyDuration.get(0));
                chatSession.setAvgReplyDuration((long) avgReplyDuration);
            }
            if (chatData.getUserEvaluate() != null) {
                BeanUtil.copyProperties(chatData.getUserEvaluate(), chatSession, CopyOptions.create().ignoreNullValue());
            }
        }
        user.setStatus(ChatConstant.user_status_offline);
        user.setOfflineTime(currentTime);
        chatCacheService.setUser(user);
        ChatAdmin admin = chatCacheService.getAdmin(user.getAdminId());
        String msgId = IdUtil.simpleUUID();
        long msgTime = System.currentTimeMillis();
        
        //结束会话消息
		/*
		 * ChatSessionMsg msgLog = ChatSessionMsg.builder().id(msgId)
		 * .userId(user.getUserId()).sessionId(sessionId) .tenantId(user.getTenantId())
		 * .senderType(0) .receiveType(0) .msgType(20)
		 * .msgContent(String.valueOf(outType)) .msgTime(msgTime)
		 * .adminId(admin.getAdminId()) .build();
		 * chatSessionMsgService.insertSessionMsg(msgLog);
		 */
        
        chatSessionService.insertOrUpdateSession(chatSession);
        logger.info("user_out_end:{},{},{}", user, chatSession, outType);
    }

    public void evaluate(UserEvaluate param) {
        String userId = param.getUserId();
        ChatUser chatUser = chatCacheService.getUser(userId);
        String sessionId = chatUser.getSessionId();
        ChatData chatData = chatCacheService.getData(sessionId);
        if (chatData == null) {
            return;
        } else {
            if (chatUser.getStatus() == ChatConstant.admin_status_offline) {
                //离线状态直接更新数据库
                ChatSession chatSession = BeanUtil.toBean(param, ChatSession.class);
                chatSession.setSessionId(sessionId);
                chatSessionService.update(chatSession);
            } else {
                //非离线状态更新缓存，用户下线时候会取缓存入库
                chatData.setUserEvaluate(param);
                chatCacheService.setData(chatData);
            }
            if (param.getEvaluateAdminMode() != null && param.getEvaluateAdminMode() == 1) {
                AdminUserEvaluated msg = new AdminUserEvaluated();
                msg.setUserId(userId);
                msg.setSessionId(sessionId);
                ChatAdmin admin = chatCacheService.getAdmin(chatUser.getAdminId());
                chatCacheService.sendEventMsg(admin.getPollId(), msg);
            }
        }
        logger.info("user_evaluate:{},{},{}", param, chatUser, chatData);
    }
    
    public Boolean isEvaluate(UserEvaluate param) {
    	String userId = param.getUserId();
    	ChatUser chatUser = chatCacheService.getUser(userId);
    	String sessionId = chatUser.getSessionId();
    	ChatData chatData = chatCacheService.getData(sessionId);
    	if(chatData==null) {
    		return false;
    	}
    	UserEvaluate userEvaluate=chatData.getUserEvaluate();
    	if(userEvaluate==null) {
    		return false;
    	}
    	if(chatUser.getStatus()==ChatConstant.user_status_robot) {
    		return userEvaluate.getEvaluateRobotMode()!=null&&userEvaluate.getEvaluateRobotMode()==1;
    	}else if(chatUser.getStatus()==ChatConstant.user_status_online) {
    		return userEvaluate.getEvaluateAdminMode()!=null&&userEvaluate.getEvaluateAdminMode()==1;
    	}
    	return false; 
    	
    }

    public MultiPart upload(MultipartFile file, String userId) {
        ChatUser chatUser = chatCacheService.getUser(userId);
        return multipartProvider.upload(file, "chat/visitor", chatUser.getTenantId());
    }

    public void typing(UserTypingP param) {
        ChatUser chatUser = chatCacheService.getUser(param.getUserId());
        if (chatUser == null || chatUser.getStatus() != ChatConstant.user_status_online) {
            return;
        }
        AdminUserTyping msg = new AdminUserTyping();
        msg.setUserId(param.getUserId());
        msg.setSessionId(chatUser.getSessionId());
        msg.setMsgContent(param.getMsgContent());
        ChatAdmin admin = chatCacheService.getAdmin(chatUser.getAdminId());
        chatCacheService.sendEventMsg(admin.getPollId(), msg);
    }

     
    public void leaveMsg(ChatLeaveMsgP param) {
        Instant start = Instant.now();
        String id = param.getChannelId();
        ChatChannel chatChannel = chatChannelService.getFromId(id);
        Assert.isNull(chatChannel, "渠道信息异常！");
        Assert.isTrue(1 != chatChannel.getLeaveMsgMode(), "渠道未开启留言功能！");
        ChatUser user = chatCacheService.getUser(param.getUserId());
        ChatLeaveMsg info = BeanUtil.toBean(param, ChatLeaveMsg.class);
        info.setMsgId(IdUtil.simpleUUID());
        info.setPid(user.getPid());
        info.setTenantId(user.getTenantId());
        info.setSessionId(user.getSessionId());
        info.setChannelId(user.getChannelId());
        info.setChannelName(user.getChannelName());
        info.setCreateTime(System.currentTimeMillis());
        chatLeaveMsgService.save(info);
        logger.info("客户留言:{}{}{}ms", param, JacksonUtil.toString(info), Duration.between(start, Instant.now()).toMillis());
    }

     
    public void submitUserForm(SubmitUserFormP param) {
        ChatUser user = chatCacheService.getUser(param.getUserId());
        BeanUtil.copyProperties(param, user, CopyOptions.create().ignoreNullValue());
        logger.info("user_submitUserForm:{},{}", param, user);
        user.setUserFormMode(1);
        chatCacheService.setUser(user);
    }

     
    public void beforeUnload(UserP param) {
        ChatUser user = chatCacheService.getUser(param.getUserId());
        logger.info("user_beforeUnload:{}", user);
//        ThreadUtil.safeSleep(23 * 1000);
//        Object time = chatCacheService.getUserPollTime(user.getTenantId(), user.getUserId());
//        if (user.getStatus() != ChatConstant.user_status_offline && time != null && Long.parseLong(String.valueOf(time)) < System.currentTimeMillis() - 20 * 1000) {
//            //等待一段时间以后，如果用户轮询时间没有来更新，说明用户已经离开页面了，下线处理
//            logger.info("user_beforeUnload_out:{},{}", user, time);
//            out(user.getUserId(), ChatConstant.out_type_close);
//        }
    }


     
    public MultiPart uploadBase64Image(ChatUserBase64Vo param) {
        Assert.isEmpty(param.getUserId(), "参数异常！");
        Assert.isEmpty(param.getImgData(), "参数异常，无法上传！");
        ChatUser chatUser = chatCacheService.getUser(param.getUserId());
        Assert.isNull(chatUser, "客户信息异常！");
        String imgData = param.getImgData();
        MultiPart multiPart = null;
        try {
            String data;
            String suffix;
            String[] d = imgData.split("base64,");//将字符串分成数组
            if (d != null && d.length == 2) {
                suffix = d[0];
                data = d[1];
            } else {
                throw BusinessException.msg( "上传图片格式不合法");
            }
            if ("data:image/jpeg;".equalsIgnoreCase(suffix)) {
                suffix = "jpg";
            }
            if ("data:image/x-icon;".equalsIgnoreCase(suffix)) {
                suffix = "ico";
            }
            if ("data:image/gif;".equalsIgnoreCase(suffix)) {
                suffix = "gif";
            }
            if ("data:image/png;".equalsIgnoreCase(suffix)) {
                suffix = "png";
            }
            Assert.isEmpty(suffix, "不符合图片规则后缀！");
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            BufferedImage bufferedImage = ImgUtil.toImage(data);
            ImageIO.write(bufferedImage, suffix, os);
            multiPart = multipartProvider.upload(new ByteArrayInputStream(os.toByteArray()), "chat/visitor", chatUser.getTenantId(), suffix);
            multiPart.setSuffix(suffix);
        } catch (Exception e) {
            throw BusinessException.msg( "上传失败");
        }
        return multiPart;
    }

     
    public ChatUserParam.GetRobotDocR getRobotDoc(ChatUserParam.GetRobotDocP param) {
        RobotDoc robotDoc = robotDocMapper.selectById(param.getDocId());
        return BeanUtil.toBean(robotDoc, ChatUserParam.GetRobotDocR.class);
    }

     
    public void msgComplete(UserP param) {
        ChatUser user = chatCacheService.getUser(param.getUserId());
        ChatReceptConfig chatReceptConfig = chatReceptConfigService.getConfig(user.getAdminId());
        if (Objects.equals(chatReceptConfig.getReadFlag(), 1)) {
            ThreadUtil.execute(() -> {
                logger.info("msgComplete:{}", user);
                List<ChatSessionMsg> msgList=chatSessionMsgService.list(Wrappers.<ChatSessionMsg>lambdaQuery()
                        .eq(ChatSessionMsg::getSessionId, user.getSessionId())
                        .eq(ChatSessionMsg::getReadFlag, 0)
                        .eq(ChatSessionMsg::getSenderType, 2)
                );
                for (ChatSessionMsg msg : msgList) {
                    //给客服发送消息已读
                    ChatAdmin admin = chatCacheService.getAdmin(user.getAdminId());
                    if (admin!=null && admin.getStatus()!=ChatConstant.admin_status_offline){
                        AdminGetMsgRead msgRead = new AdminGetMsgRead();
                        msgRead.setMsgId(msg.getId());
                        msgRead.setSessionId(user.getSessionId());
                        msgRead.setUserId(user.getUserId());
                        chatCacheService.sendEventMsg(admin.getPollId(), msgRead);
                    }
                    //更新聊天消息为已读
                    chatSessionMsgService.update(Wrappers.lambdaUpdate(ChatSessionMsg.class).set(ChatSessionMsg::getReadFlag, 1)
                            .eq(ChatSessionMsg::getId, msg.getId()));
                }
            });
        }

    }

    public UserInitR changeUser(HttpServletRequest request, UserChangeP param) {

    	String userId = param.getUserId();
        Assert.isEmpty(userId, "参数错误");
        
        ChatUser userCache = chatCacheService.getUser(userId);
        Assert.isNull(userCache,"系统错误");
        
        String ip=IpAddressUtil.getIpAddr(request);
        Assert.isNotEquals(userCache.getUserIp(), ip, "系统错误");
        Assert.isNotEquals(userCache.getPollId(), param.getPollId(), "系统错误");
       // Assert.isNotEquals(userCache.getStatus(), ChatConstant.user_status_online, "系统错误");
        
        userCache.setUserName(StrUtil.isNotBlank(param.getUserName())?param.getUserName():userCache.getUserName());
        userCache.setPhone(StrUtil.isNotBlank(param.getPhone()) ? param.getPhone() : userCache.getPhone());
        userCache.setEmail(StrUtil.isNotBlank(param.getEmail()) ? param.getEmail() : userCache.getEmail());
        userCache.setPid(StrUtil.isNotBlank(param.getPid()) ? param.getPid() : userCache.getPid());
        userCache.setParams(StrUtil.isNotBlank(param.getParams()) ? param.getParams() : userCache.getParams());
        userCache.setVipLevel(param.getVipLevel()==null ? param.getVipLevel() : userCache.getVipLevel());
       
        chatCacheService.setUser(userCache);
        UserChangeMsg changeMsg=new UserChangeMsg();
        changeMsg.setSessionId(userCache.getSessionId());
        changeMsg.setUserId(userCache.getUserId());
        ChatAdmin admin = chatCacheService.getAdmin(userCache.getAdminId());
        chatCacheService.sendEventMsg(admin.getPollId(), changeMsg);
        logger.info("user_change_end:{},{}", param, userCache);
        return BeanUtil.toBean(userCache, UserInitR.class);
    }

    public GetTokenResult getVideoToken(UserP param) {
        //判断当前用户是否在redis中是否存在
        if (redisTemplate.hasKey(ChatConstant.chat_rongCloud_key+param.getUserId())){
            String userResult = redisTemplate.opsForValue().get(ChatConstant.chat_rongCloud_key+param.getUserId());
            GetTokenResult result = JacksonUtil.toBean(userResult, GetTokenResult.class);
            return result;

        }else {
            ChatUser user = chatCacheService.getUser(param.getUserId());
            HashMap<String, Object> map = new HashMap<>();
            map.put("userId",user.getUserId());
            map.put("name",user.getUserName());
            GetTokenResult result = RongCloudUtils.requestCloud(map);
            if (result!=null&&result.getCode().equals(200)){
                redisTemplate.opsForValue().set(ChatConstant.chat_rongCloud_key+user.getUserId(), JacksonUtil.toString(result));
                redisTemplate.expire(ChatConstant.chat_rongCloud_key+user.getUserId(), 24, TimeUnit.HOURS);
            }
            return result;
        }

    }

    public void inviteVideo(ChatUserParam.UserVideoInvitation param) {
        if (param.getVideoType()==2){
            ChatEventMsg.AdminVideoInvitationMsg msg = new ChatEventMsg.AdminVideoInvitationMsg();
            ChatUser user = chatCacheService.getUser(param.getUserId());
            ChatAdmin admin = chatCacheService.getAdmin(user.getAdminId());
            msg.setName(user.getUserName());
            msg.setUserId(user.getUserId());
            logger.info("user_inviteAdminVideo:{},{},{}",param,user,admin);

            chatCacheService.sendEventMsg(admin.getPollId(),msg);
        }else {
            ChatEventMsg.AdminVideoTypeMsg msg = new ChatEventMsg.AdminVideoTypeMsg();
            ChatUser user = chatCacheService.getUser(param.getUserId());
            ChatAdmin admin = chatCacheService.getAdmin(user.getAdminId());
            msg.setName(user.getUserName());
            msg.setUserId(user.getUserId());
            logger.info("user_inviteAdminAudio:{},{},{}",param,user,admin );
            chatCacheService.sendEventMsg(admin.getPollId(),msg);
        }

    }

    public Boolean expireVideoToken(ChatRongYunExpireTokenVo vo) {
        //获取需要销毁的token
        String userId = vo.getUserId();
        if (userId.isEmpty()){
            throw new RuntimeException("id不能为空");
        }
        HashMap<String, Object > map  = new HashMap<String, Object>(){{
            put("userId",userId);
            put("time", String.valueOf(Calendar.getInstance().getTimeInMillis()));
        }};
        GetTokenResult result = new RongCloudUtils().requestExpireToken(map);
        if (result!=null){
            //删除redis中存放的数据
                redisTemplate.delete(ChatConstant.chat_rongCloud_key + userId);
            return true;
        }
        return false;
    }

    public boolean videoStatus(ChatAdminParam.AdminUserP param) {
        //获取客服id
        String userId = param.getUserId();
        String s = redisTemplate.opsForValue().get(ChatConstant.chat_rongCloud_key + userId);

        if (s!=null){
            return false;
        }
        else {
            return true;
        }
    }

    public boolean commentDoc(ChatUserParam.UserCommentDoc param) {
        logger.info("词条评论:{}", param);
        Long count = robotDocCommentMapper.selectCount(Wrappers.lambdaQuery(RobotDocComment.class)
                .eq(RobotDocComment::getSessionId, param.getSessionId())
                .eq(RobotDocComment::getDocId, param.getDocId())
                .eq(RobotDocComment::getMsgId, param.getMsgId()));
        Assert.isTrue(count > 0, "已反馈！");
        ChatUser chatUser = chatCacheService.getUser(param.getUserId());
        RobotDocComment info = param.convert();
        info.setTenantId(chatUser.getTenantId());
        robotDocCommentMapper.insert(info);
        chatSessionMsgService.update(Wrappers.lambdaUpdate(ChatSessionMsg.class)
                .set(ChatSessionMsg::getDocComment, param.getIsUseful()).eq(ChatSessionMsg::getId, param.getMsgId()));
        return true;
    }

    public String sysMsg(ChatUserParam.UserSysMsgP param) {
        String msgId = IdUtil.simpleUUID();
        ThreadUtil.execute(() -> {
            ChatUser user = chatCacheService.getUser(param.getUserId());
            if(user==null){
                return;
            }
            String sessionId = user.getSessionId();
            long msgContentCount = chatSessionMsgService.lambdaQuery().eq(ChatSessionMsg::getMsgContent, param.getMsgContent())
                    .eq(ChatSessionMsg::getSessionId, sessionId).eq(ChatSessionMsg::getSenderType,4).count();
            if(msgContentCount>0){
                return;
            }
            ChatSessionMsg chatSessionMsg = ChatSessionMsg.builder()
                    .senderType(4).msgType(1).msgContent(param.getMsgContent()).clientType(param.getClientType()).readFlag(1)
                    .id(msgId).sessionId(sessionId).userId(user.getUserId()).adminId(user.getAdminId())
                    .tenantId(user.getTenantId()).msgTime(System.currentTimeMillis()).build();
            chatSessionMsgService.save(chatSessionMsg);
            logger.info("访客端存储系统消息:{},{}",param, chatSessionMsg);
        });
        return msgId;
    }
}
